home *** CD-ROM | disk | FTP | other *** search
/ Fritz: All Fritz / All Fritz.zip / All Fritz / FILES / PROGBLER / A86V318C.LZH / A11.DOC < prev    next >
Text File  |  1988-05-20  |  32KB  |  833 lines

  1. CHAPTER 11   MACROS AND CONDITIONAL ASSEMBLY
  2.  
  3.  
  4. Macro Facility
  5.  
  6. A86 contains an easy-to-use, but very powerful macro facility.
  7. The facility subsumes the capabilities of most assemblers,
  8. including operand concatenation, repeat, indefinite repeat (often
  9. called IRP), indefinite repeat character (IRPC), passing macro
  10. operands by text or by value, comparing macro operands to
  11. strings, and detecting blank macro operands.  Unlike other
  12. assemblers, A86 integrates these functions into the main macro
  13. facility; so they can be invoked without clumsy syntax, or
  14. strange characters in the macro call operands.
  15.  
  16.  
  17. Simple Macro Syntax
  18.  
  19. All macros must be defined before they are used.  A macro
  20. definition consists of the name of the macro, followed by the
  21. word MACRO, followed by the text of the macro, followed by #EM,
  22. which marks the end of the macro.
  23.  
  24. Many assembly languages require a list of dummy operand names to
  25. follow the word MACRO.  A86 does not: the operands are denoted in
  26. the text with the fixed names #1, #2, #3, ... up to a limit of
  27. #9, for each operand in order.  If there is anything following
  28. the word MACRO, it is considered part of the macro text.
  29.  
  30. Examples:
  31.  
  32. ; CLEAR sets the register operand to zero.
  33.  
  34. CLEAR  MACRO  SUB #1,#1 #EM
  35.  
  36.   CLEAR AX      ; generates a SUB AX,AX instruction
  37.   CLEAR BX      ; generates a SUB BX,BX instruction
  38.  
  39.  
  40. ; MOVM moves the second operand to the first operand.
  41. ;   Both operands can be memory variables.
  42.  
  43. MOVM  MACRO
  44.   MOV AL,#2
  45.   MOV #1,AL
  46. #EM
  47.  
  48. VAR1  DB  ?
  49. VAR2  DB  ?
  50.  
  51. MOVM VAR1,VAR2  ; generates MOV AL,VAR2 followed by MOV VAR1,AL
  52.                                                              11-2
  53.  
  54. Formatting in Macro Definitions and Calls
  55.  
  56. The format of a macro definition is flexible.  If the macro text
  57. consists of a single instruction, the definition can be given in
  58. a single line, as in the CLEAR macro given above.  There is no
  59. particular advantage to doing this, however: A86 prunes all
  60. unnecessary spaces, blank lines, and comments from the macro text
  61. before entering the text into the symbol table.  I recommend the
  62. more spread-out format of the MOVM macro, for program
  63. readability.
  64.  
  65. All special macro operators within a macro definition begin with
  66. a hash sign # (a hex 23 byte).  The letters following the hash
  67. sign can be given in either upper case or lower case.  Hash-sign
  68. operators are recognized even within quoted strings.  If you wish
  69. the hash sign to be treated literally, and not as the start of a
  70. special macro operator, you must give 2 consecutive hash signs:
  71. ##.  For example:
  72.  
  73. FOO MACRO
  74.   DB '##1'
  75.   DB '#1'
  76. #em
  77.  
  78. FOO abc      ; produces  DB '#1'  followed by  DB 'abc'
  79.  
  80. The format of the macro call line is also flexible.  A macro call
  81. consists of the name of the macro, followed by the operands to be
  82. plugged into the macro.  A86 prunes leading and trailing blanks
  83. from the operands of a macro call.  The operands to a macro call
  84. are always separated by commas.  Also, as in all A86 source
  85. lines, a semi-colon occurring outside of a quoted string is the
  86. start of a comment, ignored by A86.  If you want to include
  87. commas, blanks, or semi-colons in your operands, you must enclose
  88. your operand in single quotes.
  89.  
  90.  
  91.  
  92. Macro Operand Substitution
  93.  
  94. Some macro assemblers expect the operands to macro calls to
  95. follow the same syntax as the operands to instructions.  In those
  96. assemblers, the operands are parsed, and reduced to numeric
  97. values before being plugged into the macro definition text.  This
  98. is called "passing by value".  As its default, A86 does not pass
  99. by value, it passes by text.  The only parsing of operands done
  100. by the macro processor is to determine the start and the finish
  101. of the operand text.  That text is substituted, without regard
  102. for its contents, for the "#n" that appears in the macro
  103. definition.  The text is interpreted by A86 only after a complete
  104. line is expanded and as it is assembled.
  105.                                                              11-3
  106.  
  107. If the first non-blank character after the macro name is a comma,
  108. then the first operand is null: any occurrences of #1 in the
  109. macro text will be deleted, and replaced with nothing.  Likewise,
  110. any two consecutive commas with no non-blanks between them will
  111. result in the corresponding null operand.  Also, out-of-range
  112. operands are null; for example, #3 is a null operand if only two
  113. operands are provided in the call.
  114.  
  115. Null operands to macros are not in themselves illegal.  They will
  116. produce errors only if the resulting macro expansion is illegal.
  117.  
  118. The method of passing by text allows operand text to be plugged
  119. anywhere into a macro, even within symbol names.  For example:
  120.  
  121. ; KF_ENTRY creates an entry in the KFUNCS table, consisting of a
  122. ;   pointer to a KF_ action routine.  It also declares the
  123. ;   corresponding CF_ symbol, which is the index within the table
  124. ;   for that entry.
  125.  
  126. KF_ENTRY  MACRO
  127.   CF_#1  EQU  ($-KFUNCS)/2+080
  128.   DW  KF_#1
  129. #EM
  130.  
  131. KFUNCS:
  132.   KF_ENTRY  UP
  133.   KF_ENTRY  DOWN
  134.  
  135. ; The above code is equivalent to:
  136. ;
  137. ;  KFUNCS:
  138. ;    DW KF_UP
  139. ;    DW KF_DOWN
  140. ;
  141. ;  CF_UP    EQU  080
  142. ;  CF_DOWN  EQU  081
  143.  
  144.  
  145.  
  146. Quoted String Operands
  147.  
  148. As mentioned before, if you want to include blanks, commas, or
  149. semicolons in your operands, you enclose the operand in single
  150. quotes.  In the vast majority of cases in which these special
  151. characters need to be part of operands, the user wants them to be
  152. quoted in the final, assembled line also.  Therefore, the quotes
  153. are passed in the operand.  To override this, and strip the
  154. quotes from the string, you precede the quoted string with a hash
  155. sign.  Examples:
  156.                                                              11-4
  157.  
  158. DBW  MACRO
  159.   DB  #1
  160.   DW  #2
  161. #EM
  162.  
  163. DBW  'E', E_POINTER
  164. DBW  'W', W_POINTER
  165.  
  166. ; note that if quotes were not passed, the above lines would have
  167. ;   to be   DBW  '''E''', E_POINTER;    DBW  '''W''', W_POINTER
  168.  
  169. FETCH_CHAR  MACRO
  170.   LODSB
  171.   #1
  172.   CALL PROCESS_CHAR
  173. #EM
  174.  
  175. FETCH_CHAR  STOSB      ; generates STOSB as second instruction
  176. FETCH_CHAR  #'INC DI'  ; generates INC DI as second instruction
  177.  
  178.  
  179.  
  180. Looping by Operands in Macros
  181.  
  182. A86's macro facility contains two kinds of loops: you can loop
  183. once for each operand in a range of operands; or you can loop
  184. once for each character within an operand.  The first kind of
  185. loop, the R-loop, is discussed in this section; the second kind,
  186. the C-loop, is discussed later.
  187.  
  188. An R-loop is a stretch of macro-definition code that is repeated
  189. when the macro is expanded.  In addition to the fixed operands #1
  190. through #9, you can specify a variable operand, whose number
  191. changes each time through the loop.  You give the variable
  192. operand one of the 4 names #W, #X, #Y, or #Z.
  193.  
  194. An R-loop begins with #R, followed immediately by the letter
  195. W,X,Y, or Z naming the variable, followed by the number of the
  196. first operand to be used, followed by the number of the last
  197. operand to be used.  After the #Rxnn is the text to be repeated.
  198. The R-loop ends with #ER.  For example:
  199.  
  200. STORE3 MACRO
  201.   MOV AX,#1
  202. #RY24               ; "repeat for Y running from 2 through 4"
  203.   MOV #Y,AX
  204. #ER
  205. #EM
  206.  
  207. STORE3  VAR1,VAR2,VAR3,VAR4
  208.  
  209. ; the above call produces the 4 instructions MOV AX,VAR1; MOV VAR2,AX;
  210. ;   MOV VAR3,AX; MOV VAR4,AX.
  211.                                                              11-5
  212.  
  213. The #L Last Operator and Indefinite Repeats
  214.  
  215. A86 recognizes the special operator #L, which is the last operand
  216. in a macro call.  #L can appear anywhere in macro text; but its
  217. big power occurs in conjunction with R-loops, to yield an
  218. indefinite-repeat facility.
  219.  
  220. A common example is as follows: you can take any macro that is
  221. designed for one operand, and easily convert it into a macro that
  222. accepts any number of operands.  You do this by placing the
  223. command #RX1L, "repeat for X running from 1 through L", at the
  224. start of the macro, and the command #ER at the end just before
  225. the #EM.  Finally, you replace all instances of #1 in the macro
  226. with #X.  We see how this works with the CLEAR macro:
  227.  
  228. CLEAR MACRO #RX1L
  229.   SUB #X,#X
  230. #ER
  231. #EM
  232.  
  233. CLEAR AX,BX  ; generates both SUB AX,AX and SUB BX,BX in one macro!
  234.  
  235. It is possible for R-loops to iterate zero times.  In this case,
  236. the loop-text is skipped completely.  For example, CLEAR without
  237. any operands would produce no expanded text.
  238.  
  239.  
  240. Character Loops
  241.  
  242. We have seen the R-loop; now we discuss the other kind of loop in
  243. macros, the character loop, or C-loop.  In the C-loop, the
  244. variable W,X,Y, or Z does not represent an entire operand; it
  245. represents a character within an operand.
  246.  
  247. You start a C-loop with #C, followed by one of the 4 letters
  248. W,X,Y, or Z, followed by a single operand specifier-- a digit,
  249. the letter L, another one of W,X, Y, or Z defined in an outer
  250. loop, or one of the more complicated specifiers defined later in
  251. this chapter.  Following the #Cxn is the text of the C-loop.  The
  252. C-loop ends with #EC. The macro will loop once for every
  253. character in the operand. That single character will be
  254. substituted for each instance of the indicated variable operand.
  255. For example:
  256.  
  257. PUSHC  MACRO  #CW1
  258.   PUSH #WX
  259. #EC#EM
  260.  
  261. PUSHC ABC ; generates 3 instructions PUSH AX | PUSH BX | PUSH CX
  262.  
  263. If the C-operand is quoted (using single-quotes, not double-
  264. quotes) in the macro call, the quotes ARE removed from the
  265. operand before passing characters to the loop. It is not
  266. necessary to precede the quoted string with a hash sign in this
  267. case.  If you do, the hash sign will be passed as the first
  268. character.
  269.                                                              11-6
  270.  
  271. In the present version of A86, do not use double-quotes when
  272. quoting a C-operand.  I will eventually provide the same
  273. functionality as single quotes, but I don't do that in this
  274. version.
  275.  
  276. If the C-operand is a null operand (no characters in it), the
  277. loop text is skipped completely.
  278.  
  279.  
  280. The "B"-Before and "A"-After Operators
  281.  
  282. So far, we have seen that you can specify operands in your macro
  283. in fourteen different ways: 1,2,3,4,5,6,7,8,9,W,X,Y,Z,L.  We now
  284. multiply these 14 possibilities, by introducing the "A" and "B"
  285. operators.  You can precede any of the 14 specifiers with "A" or
  286. "B", to get the adjacent operand after or before the specified
  287. operand.  For example, BL means the operand just before the last
  288. operand; in other words, the second-to-the-last operand.  AZ
  289. means the operand just after the Z operand.  You can even repeat,
  290. up to a limit of 4 "B"s or 3 "A"s:  for example, BBL is the
  291. third-to-last operand.
  292.  
  293. Note that any operand specifier can appear in contexts other than
  294. by itself following a # within a macro.  For example, BBL could
  295. appear as the upper limit to an R-loop: #RZ1BBL loops with Z
  296. running from the first operand to the third-to-last operand.
  297.  
  298. In the case of the variable operand to a C-loop, the "A" and "B"
  299. specifiers denote the characters before or after the current
  300. looping-character.  An example of this is given in the next
  301. section.
  302.  
  303.  
  304. Multiple Increments within Loops
  305.  
  306. We have seen that you end an R-loop with a #ER, and you end a
  307. C-loop with a #EC.  We now present another way to end these
  308. loops; a way that lets you specify a larger increment to the
  309. macro's loop counter.  You can end your loops with one of the 4
  310. additional commands #E1, #E2, #E3, or #E4.
  311.  
  312. For R-loops terminated by #ER, the variable operand advances to
  313. the next operand when the loop is made.  If you end your R-loop
  314. with #E2, the variable operand advances 2 operands, not just one.
  315. For #E3, it advances 3 operands; for #E4, 4 operands.  The #E1
  316. command is the same as #ER.
  317.  
  318. The most common usage of this feature is as follows:  You will
  319. recall that we generalized the CLEAR macro with the #L-variable,
  320. so that it would take an indefinite number of operands.  Suppose
  321. we want to do the same thing with the DBW macro.  We would like
  322. DBW to take any number of operands, and alternate DBs and DWs
  323. indefinitely on the operands.  This is made possible by creating
  324. an R-loop terminated by #E2:
  325.                                                              11-7
  326.  
  327. DBW  MACRO  #RX1L
  328.   DB  #X
  329.   DW  #AX
  330. #E2
  331. #EM
  332.  
  333. DBW  'E',E_POINTER,  'W',W_POINTER   ; two pairs on same line!
  334.  
  335. The #E2 terminator means that we are looping on a pair of
  336. operands.  Note the crucial usage of the "A"-after operator to
  337. specify the second operand of the operand pair.
  338.  
  339. A special note applies to the DBW macro above: A86 just happens
  340. to accept a DW directive with no operands (it generates no object
  341. code, and issues no error).  This means that DBW will accept an
  342. odd number of operands with no error, and do the expected thing
  343. (it alternates bytes and words, ending with a byte).
  344.  
  345. You could likewise generalize a macro with 3 or 4 operands, to an
  346. indefinite number of triples or quadruples; by ending the R-loop
  347. with #E3 or #E4.  The operands in each group would be specified
  348. by #X, #AX, #AAX, and, for #E4, #AAAX.
  349.  
  350. For C-loops terminated by #E1 through #E4, the character pointer
  351. is advanced the specified number of characters.  You use this in
  352. much the same way as for R-loops, to create loops on pairs,
  353. triplets, and quadruplets of characters.  For example:
  354.  
  355. PUSHC2  MACRO  #CZ1
  356.   PUSH #Z#AZ
  357. #E2
  358. #EM
  359.  
  360. PUSHC2  AXBXSIDI  ; generates PUSH AX | PUSH BX | PUSH SI | PUSH DI
  361.  
  362.  
  363. Negative R-loops
  364.  
  365. We now introduce another form of R-loop, called the Q-loop-- the
  366. negative repeat loop.  This loop is the same as the R-loop,
  367. except that the operand number decrements instead of increments;
  368. and the loop exits when the number goes below the finish-number,
  369. not above it.  The Q-loop is specified by #Qxnn instead of #Rxnn,
  370. and #EQ instead of #ER.  You can also use the multiple-decrement
  371. forms #E1 #E2 #E3 or #E4 to terminate an Q-loop.
  372.  
  373. Example:
  374.  
  375. MOVN MACRO #QXL2   ; "negative repeat X from L down to 2"
  376.   MOV #BX,#X
  377. #EQ#EM
  378.  
  379. MOVN AX,BX,CX,DX   ; generates the three instructions:
  380.                    ;    MOV CX,DX
  381.                    ;    MOV BX,CX
  382.                    ;    MOV AX,BX
  383.                                                              11-8
  384.  
  385. Note: the above functionality is already built into the MOV
  386. instruction of A86.  The macro shows how you would implement it
  387. if you did not already have this facility.
  388.  
  389.  
  390. Nesting of Loops in Macros
  391.  
  392. A86 allows nesting of loops within each other. Since we provide
  393. the 4 identifiers W,X,Y,Z for the loop operands, you can nest to
  394. a level of 4 without restriction-- just use a different letter
  395. for each nesting level.  You can nest even deeper, for example,
  396. by having two nested R-loops that use W is its indexing letter.
  397. The only restriction to this is that you cannot refer to the W of
  398. the outer loop from within the inner W loop.  (I challenge anyone
  399. to come up with an application in which these limitations /
  400. restrictions cause a genuine inconvenience!)
  401.  
  402.  
  403. Implied Closing of Loops
  404.  
  405. If you have a loop or loops ending when the macro ends, and if
  406. the iteration count for those loops is 1, you may omit the #ER,
  407. #EC, or #EQ.  A86 closes all open loops when it sees #EM, with no
  408. error.
  409.  
  410. For example, if you omit the #ER for the loop version of the
  411. CLEAR macro, it would make no difference-- A86 automatically
  412. places an #ER code into the macro definition for you.
  413.  
  414.  
  415. Passing Operands by Value
  416.  
  417. As already stated, A86's defualt mode for passing operands is by
  418. text-- the characters of the operand are copied to the macro
  419. expansion line as-is, without any evaluation.  You may override
  420. this with the #V operator.  When A86 sees #Vn in a macro
  421. definition, it will evaluate the expression given in the text of
  422. operand n, and pass a string representing the decimal constant
  423. answer, instead of the original text.  The operand must evaluate
  424. to an absolute constant value, less than 65536.  For example:
  425.  
  426. JLV MACRO
  427.   J#1 LABEL#V2
  428. #EM
  429.  
  430. JINDEX = 3
  431. JLV NC,JINDEX+1     ; generates JNC LABEL4
  432. JINDEX = 6
  433. JLV Z,JINDEX+2      ; generates JZ LABEL8
  434.                                                              11-9
  435.  
  436. Passing Operand Size
  437.  
  438. The construct #Sn is translated by A86 into the decimal string
  439. representing the number of characters in operand n.  One use of
  440. this would be to make a conditional-assembly test of whether an
  441. operand was passed at all, as we'll see later in this chapter.
  442. Another use is to generate a length byte preceding a string, as
  443. required by some high-level languages such as Turbo Pascal.
  444. Example:
  445.  
  446. LSTRING MACRO
  447.   DB #S1,'#1'
  448. #EM
  449.  
  450. LSTRING  SAMPLE     ; generates  DB 6,'SAMPLE'
  451.  
  452.  
  453. Generating the Number of an Operand
  454.  
  455. The construct #Nn is translated by A86 into the decimal string
  456. represented by the position number n of the macro operand.  Note
  457. that this value does not depend on the contents of the operand
  458. that was passed to the macro.  Thus, for example, #N2 would
  459. translate simply to 2; so this usage of #N is silly.  #N achieves
  460. usefulness when n is variable: W,X,Y,Z, or L.  I give an example
  461. of #N with a loop-control variable in the next section. Here is
  462. an example of #NL, used to generate an array of strings, preceded
  463. by a byte telling how many strings are in the array:
  464.  
  465. ZSTRINGS MACRO
  466.   DB #NL           ; generates the number of operands passed
  467. #RX1L
  468.   DB '#X',0
  469. #EM
  470.  
  471. ZSTRINGS  TOM,DICK,HARRY   ; generates DB 3 followed by strings
  472.  
  473.  
  474. Parenthesized Operand Numbers
  475.  
  476. We've seen that macro operands are usually specified in your
  477. macro definition by a single character: either a single digit or
  478. one of the special letters W,X,Y,Z, or L.  A86 also allows you to
  479. specify a constant operand number up to 255.  You do so by giving
  480. an expression enclosed in parentheses, rather than a single
  481. character.  The expession must evaluate at the time the macro is
  482. defined, to a constant between 0 and 255.  You can use this
  483. feature to translate many programs that use MASM's REPT
  484. directive.  For example, if the following REPT construct occurs
  485. within a MASM macro:
  486.  
  487.   TEMP = 0
  488. REPT 100
  489.   TEMP = TEMP + 1     ; MASM needs an explicitly-set-up counter
  490.   DB TEMP
  491. ENDM
  492.  
  493. you may translate it into an A86 loop, as follows:
  494.                                                             11-10
  495.  
  496. #RX1(100)      ; the counter X is built into the A86 loop
  497.   DB #NX
  498. #ER
  499.  
  500. If the REPT does not occur within a macro, you must define a
  501. macro containing the loop, which you may then immediately call.
  502.  
  503. Note that the expression enclosed in praentheses must not itself
  504. contain any macro operators.  Thus, for example, you cannot
  505. specify #(#NY+1) to represent the operand after Y-- you must use
  506. #AY.
  507.  
  508.  
  509. Exiting from the Middle of a Macro
  510.  
  511. For MASM compatibility, A86 offers the #EX operator, which is
  512. equivalent to MASM's EXITM directive.  #EX is typically used in a
  513. conditional assembly block within a loop, to terminate the loop
  514. early.  When the #EX code is seen in a macro expansion, the
  515. expansion ceases at that point, and assembly returns to the
  516. source file (or to the outer macro in a nested call).  You
  517. couldn't use #EM to do this, because that would signal the end of
  518. the macro definition, not just the call.
  519.  
  520.  
  521. Local Labels in Macros
  522.  
  523. Some assemblers have a LOCAL pseudo-op that is used in
  524. conjunction with macros.  Symbols declared LOCAL to a macro have
  525. unique (and bizarre) symbol names substituted for them each time
  526. the macro is called.  This solves the problem of duplicate label
  527. definitions when a macro is called more than once.
  528.  
  529. In A86, the problem is solved more elegantly, by having a class
  530. of generic local labels throughout assembly, not just in macros.
  531. Recall that symbols consisting of a single letter, followed by
  532. one or more decimal digits, can be redefined.  You can use such
  533. labels in your macro definitions.
  534.  
  535. I have recommended that local labels outside of macros be
  536. designated L1 through L9.  Within macro definitions, I suggest
  537. that you use labels M1 through M9.  If you used an Ln-label
  538. within a macro, you would have to make sure that you never call
  539. the macro within the range of definition of another Ln-label with
  540. the same name.  By using Mn-labels, you avoid such potential
  541. conflicts.
  542.  
  543. The following example of a local label within a macro is taken
  544. from the source of the macro processor itself:
  545.                                                             11-11
  546.  
  547. ; "JHASH label" checks to see if AL is a hash sign.  If it is,
  548. ;    it processes the hash sign term, and jumps to label.
  549. ;    Otherwise, it drops through to the following code.
  550.  
  551. JHASH MACRO
  552.   CMP AL,'##'       ; is the scanned character a hash sign?
  553.   JNE >M1           ; skip if not
  554.   CALL MDEF_HASH    ; process the hash sign
  555.   JMP #1            ; jump to the label provided
  556. M1:
  557.   #EM
  558.  
  559.   ...
  560. L3:                 ; loop here to eat empty lines, leading blanks
  561.   CALL SKIP_BLANKS  ; skip over the leading blanks of a line
  562.   INC SI            ; advance source ptr beyond the next non-blank
  563.   JHASH L3          ; if hash sign then process, and eat more blanks
  564.   CMP AL,0A         ; were the blanks terminated by a linefeed?
  565.   JE L3             ; loop if yes, nothing on this line
  566. L5:                 ; loop here after a line is seen to have contents
  567.   CMP AL,';'        ; have we reached the start of a comment?
  568.   JE L1             ; jump if yes, to consume the comment
  569.   JHASH >L6         ; if hash sign then process it; get next char
  570.   ...
  571. L6:
  572.   LODSB             ; fetch the next definition char from the source
  573.   CMP AL,' '        ; is it blank?
  574.   JA L5             ; loop if not, to process it
  575.   ...
  576.  
  577.  
  578. Debugging Macro Expansions
  579.  
  580. There is a tool called EXMAC which will help you troubleshoot
  581. program lines that call macros.  If you are not sure about what
  582. code is being generated by your macro calls, EXMAC will tell you.
  583. See Chapter 13 for details.
  584.  
  585.  
  586. Conditional Assembly
  587.  
  588. A86 has a conditional assembly feature, that allows you to
  589. specify that blocks of source code will or will not be assembled,
  590. according to the values of equated user symbols.  The controlling
  591. symbols can be declared in the program (and can thus be the
  592. result of assembly-time expressions), or they can be declared in
  593. the assembler invocation.
  594.  
  595. You should keep in mind the difference between conditional
  596. assembly, invoked by #IF, and the structured-programming feature,
  597. invoked by IF without the hash sign.  #IF tests a condition at
  598. assembly time, and can cause code to not be assembled and thus
  599. not appear in the program.  IF causes code to be assembled that
  600. tests a condition at run time, possibly jumping over code.  The
  601. skipped code will always appear in the program.
  602.                                                             11-12
  603.  
  604. All conditional assembly lines are identified by a hash sign # as
  605. the first non-blank character of a line.  The hash sign is
  606. followed by one of the four keywords IF, ELSEIF, ELSE or ENDIF.
  607.  
  608. #IF starts a conditional assembly block.  On the same line,
  609. following the #IF, you provide either a single name, or an
  610. arbitrary expression evaluating to an absolute constant.  In this
  611. context, a single name evaluates to TRUE if it is defined and not
  612. equal to the absolute constant zero.  A name is FALSE if it is
  613. undefined, or if it has been equated to zero.  An expression is
  614. TRUE if nonzero, FALSE if zero.
  615.  
  616. If the #IF expression evaluates to FALSE, then the following
  617. lines of code are skipped, up to the next matching #ELSEIF,
  618. #ELSE, or #ENDIF.  If the expression is TRUE, then the following
  619. lines of code are assembled normally.  If a subsequent matching
  620. #ELSEIF or #ELSE is encountered, then code is skipped up to the
  621. matching #ENDIF.
  622.  
  623. #ELSEIF provides a multiple-choice facility for #IF-blocks.  You
  624. can give any number of #ELSEIFs between an #IF and its matching
  625. #ENDIF.  Each #ELSEIF has a name or expression following it on
  626. the same line. If the construct following the #IF is FALSE, then
  627. the assembler looks for the first TRUE construct following an
  628. #ELSEIF, and assembles that block of code.  If there are no TRUE
  629. #ELSEIFs, then the #ELSE-block (if there is one) is assembled.
  630.  
  631. You should use the ! instead of the NOT operator in conditional
  632. assembly expressions.  The ! operator performs the correct
  633. translation of names into TRUE or FALSE values, and handles the
  634. case !undefined without reporting an error.
  635.  
  636. #ELSE marks the beginning of code to be assembled if all the
  637. previous blocks of an #IF have been skipped over.  There is no
  638. operand after the #ELSE.  There can be at most one #ELSE in an
  639. #IF-block, and it must appear after any #ELSEIFs.
  640.  
  641. #ENDIF marks the end of an #IF-block.  There is no operand after
  642. #ENDIF.
  643.  
  644. It is legal to have nested #IF-blocks; that is, #IF-blocks that
  645. are contained within other #IF-blocks.  #ELSEIF, #ELSE, and
  646. #ENDIF always refer to the innermost nested #IF-block.
  647.  
  648. As an example of conditional assembly, suppose that you have a
  649. program that comes in three versions: one for Texas, one for
  650. Oklahoma, and one for the rest of the nation.  The three programs
  651. differ in a limited number of places.  Instead of keeping three
  652. different versions of the source code, you can keep one version,
  653. and use conditional assembly on the boolean variables TEXAS and
  654. OKLAHOMA to control the assembler output.  A sample block would
  655. be:
  656.                                                             11-13
  657.  
  658. #if TEXAS
  659.   DB 0,1,2,3
  660. #elseif OKLAHOMA
  661.   DB 4,5,6,7
  662. #else
  663.   DB 8,9,10,11
  664. #endif
  665.  
  666. If a block of code is to be assembled only if TEXAS is false,
  667. then you would use the exclamation point operator:
  668.  
  669. #if !TEXAS
  670.   DB 0FF
  671. #endif
  672.  
  673.  
  674. Conditional Assembly and Macros
  675.  
  676. You may have conditional assembly blocks either in macro
  677. definitions or in macro expansions.  The only limitation is that
  678. if you have an #IF-block in a macro expansion, the entire block
  679. (i.e., the matching #ENDIF) must appear in the same macro
  680. expansion.  You cannot, for example, define a macro that is a
  681. synonym for #IF.
  682.  
  683. To have your conditional assembly block apply to the macro
  684. definition, you provide the block normally within the definition.
  685. For example:
  686.  
  687. X1 EQU 0
  688.   BAZ MACRO
  689. #if X1
  690.   DB 010
  691. #else
  692.   DB 011
  693. #endif
  694. #EM
  695.   BAZ
  696. X1 EQU 1
  697.   BAZ
  698.  
  699. In the above sequence of code, the conditional assembly block is
  700. acted upon when the macro BAZ is defined.  The macro therefore
  701. consists of the single line DB 011, with all the conditional
  702. assembly lines removed from the definition.  Thus, both
  703. expansions of BAZ produce the object-code byte of 011, even
  704. though the local label X1 has turned non-zero for the second
  705. invocation.
  706.  
  707. To have your conditional assembly block appear in the macro
  708. expansion, you must literalize the hash sign on each conditional
  709. assembly line by giving two hash signs:
  710.                                                             11-14
  711.  
  712. X1 EQU 0
  713.   BAZ MACRO
  714. ##if X1
  715.   DB 010
  716. ##else
  717.   DB 011
  718. ##endif
  719. #EM
  720.   BAZ
  721. X1 EQU 1
  722.   BAZ
  723.  
  724. Now the entire conditional assembly block is stored in the macro
  725. definition, and acted upon each time the macro is expanded. Thus,
  726. the two invocations of BAZ will produce the different object
  727. bytes 011 and 010, since X1 has become non-zero for the second
  728. expansion.
  729.  
  730. You will usually want your conditional assembly blocks to be
  731. acted upon at macro definition time, to save symbol table space.
  732. You will thus use the first form, with the single hash signs.
  733.  
  734.  
  735. Simulating MASM's Conditional Assembly Constructs
  736.  
  737. Microsoft's MASM assembler has an abundance of confusing
  738. conditional assembly directives, all of which are subsumed by
  739. A86's #IF expression evaluation policies.  IF and IFDEF are both
  740. covered by A86's #IF directive.  IFE and IFNDEF are duplicated by
  741. #IF followed by the exclamation-point (boolean negation)
  742. operator.  IFB and IFNB test whether a macro operand has been
  743. passed as blank-- they can be simulated by testing the size of
  744. the operand with the #Sn operator.  Finally, IFIDN and IFDIF do
  745. string comparisons of macro operands.  This is more generally
  746. subsumed by the string-comparison capabilities of the operators
  747. EQ, NE, and =.
  748.  
  749. Examples of translation of each of these constructs is given in
  750. the next chapter, on compatibility with other assemblers.
  751.  
  752.  
  753. Conditional Assembly and the XREF Program
  754.  
  755. Previous versions of A86 contained a warning, that XREF will not
  756. correctly handle conditional-assembly blocks controlled by
  757. variables whose values change during assembly.  Starting with
  758. V3.12, this has been corrected, by writing to the SYM file a log
  759. of each conditional-assembly test result.  XREF will consult the
  760. log to determine which blocks to consider.
  761.                                                             11-15
  762.  
  763. Declaring Variables in the Assembler Invocation
  764.  
  765. To facilitate the effective use of conditional assembly, A86
  766. allows you to declare boolean (true-false) symbols in the command
  767. line that invokes the assembler.  The declarations can appear
  768. anywhere in the list of source file names.  They are
  769. distinguished from the file names by a leading equals sign =.  To
  770. declare a symbol TRUE (value = 1), give the name after the equals
  771. sign.  DO NOT put any spaces between the equals sign and the
  772. name! To declare a symbol FALSE (value = 0), you can give an
  773. equals sign, an exclamation point, then the name.  Again, DO NOT
  774. embed any blanks!  Example: if your source files are src1.8,
  775. src2.8, and src3.8, then you can assemble with TEXAS true by
  776. invoking A86 as follows:
  777.  
  778.    a86 =TEXAS src1.8 src2.8 src3.8
  779.  
  780. You can assemble with TEXAS explicitly set to FALSE as follows:
  781.  
  782.    a86 =!TEXAS src1.8 src2.8 src3.8
  783.  
  784. Note that if TEXAS is used only as a conditional-assembly
  785. control, then you do not need to include the =!TEXAS in the
  786. invocation, because an undefined TEXAS will automatically be
  787. interpreted as false.
  788.  
  789. A user pointed out to me that it's impossible to get an
  790. equals-sign into an environment variable.  So A86 now accepts an
  791. up-arrow (hex 5E) character in place of an equals-sign for an
  792. invocation variable.
  793.  
  794.  
  795. Null Invocation Variable Names
  796.  
  797. A86 will ignore an equals-sign by itself in the invocation line,
  798. without error.  This allows you to generate assembler invocation
  799. lines using parameters that could be either boolean variable
  800. names, or null strings. For example, in the previously-mentioned
  801. TEXAS-OKLAHOMA-nation example, the program could be invoked via a
  802. .BAT file called "AMAKE.BAT", coded as follows:
  803.  
  804.       A86 =%1 *.8
  805.  
  806. You invoke A86 by typing one of the following:
  807.  
  808.       amake texas
  809.       amake oklahoma
  810.       amake
  811.  
  812. The third line will produce the assembler invocation  A86 = *.8;
  813. causing no invocation variables to be declared.  Thus both TEXAS
  814. and OKLAHOMA will be false, which is exactly what you want for
  815. the rest-of-the-nation version of the program.
  816.                                                             11-16
  817.  
  818. Changing Values of Invocation Variables
  819.  
  820. The usual prohibition against changing the value of a symbol that
  821. is not a local label does not apply to invocation variables.  For
  822. example, suppose you have a conditional control variable DEBUG,
  823. which will generate diagnostic code for debugging when it is
  824. true.  Suppose further that you have already debugged source
  825. files src1.8 and src3.8; but you are still working on src2.8. You
  826. may invoke A86 as follows:
  827.  
  828.    A86 src1.8 =DEBUG src2.8 =!DEBUG src3.8
  829.  
  830. The variable DEBUG will be TRUE only during assembly of src2.8,
  831. just as you want.
  832.  
  833.